package com.leo.foodmaster.admin.stock.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.leo.foodmaster.admin.foodMaterial.entity.FoodMaterialType;
import com.leo.foodmaster.admin.foodMaterial.service.IFoodMaterialTypeService;
import com.leo.foodmaster.admin.purchase.entity.PurchaseBill;
import com.leo.foodmaster.admin.purchase.entity.PurchaseBillDetails;
import com.leo.foodmaster.admin.purchase.entity.dto.PurchaseBillDTO;
import com.leo.foodmaster.admin.purchase.service.IPurchaseBillDetailsService;
import com.leo.foodmaster.admin.purchase.service.IPurchaseBillService;
import com.leo.foodmaster.admin.stock.entity.Warehouse;
import com.leo.foodmaster.admin.stock.entity.WarehouseInRecord;
import com.leo.foodmaster.admin.stock.entity.WarehouseInRecordDetails;
import com.leo.foodmaster.admin.stock.entity.WarehouseUser;
import com.leo.foodmaster.admin.stock.mapper.WarehouseInRecordMapper;
import com.leo.foodmaster.admin.stock.service.IWarehouseInRecordDetailsService;
import com.leo.foodmaster.admin.stock.service.IWarehouseInRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.leo.foodmaster.admin.stock.service.IWarehouseService;
import com.leo.foodmaster.admin.stock.service.IWarehouseUserService;
import com.leo.foodmaster.admin.system.entity.UserBean;
import com.leo.foodmaster.commons.constants.RecordStatus;
import com.leo.foodmaster.commons.entity.Result;
import com.leo.foodmaster.commons.util.WebUtils;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 入库记录 服务实现类
 * </p>
 *
 * @author Leo
 * @since 2021-08-21
 */
@Service
@AllArgsConstructor
public class WarehouseInRecordServiceImpl extends ServiceImpl<WarehouseInRecordMapper, WarehouseInRecord> implements IWarehouseInRecordService {
    private final IWarehouseUserService warehouseUserService;
    private final IWarehouseInRecordDetailsService warehouseInRecordDetailsService;
    private final IWarehouseService warehouseService;
    private final IFoodMaterialTypeService foodMaterialTypeService;
    private final IPurchaseBillService purchaseBillService;
    private final IPurchaseBillDetailsService purchaseBillDetailsService;

    /**
     * 条件分页查询入库记录列表
     * @param page 分页条件
     * @param warehouseId 仓库Id
     * @param serialNum 仓库编号
     * @param status 单据状态
     * @param beginTime 开始时间
     * @param endTime 结束时间
     * @return IPage<WarehouseInRecord>
     */
    @Override
    public IPage<WarehouseInRecord> getWarehouseInRecordPageList(Page<WarehouseInRecord> page, Long warehouseId,
                                                         String serialNum, String status,
                                                         String beginTime, String endTime) {
        Long userId = WebUtils.getUser().getId();
        Long organId = WebUtils.getUser().getOrganId();
        // 查询仓库用户关联表
        List<WarehouseUser> warehouseUsers = warehouseUserService.list(Wrappers.lambdaQuery(WarehouseUser.class)
                .eq(WarehouseUser::getUserId, userId));
        List<Long> warehouseIds = warehouseUsers.stream().map(WarehouseUser::getWarehouseId).collect(Collectors.toList());
        if (BeanUtil.isNotEmpty(warehouseId)) {
            warehouseIds = new ArrayList<>();
            warehouseIds.add(warehouseId);
        }
        LambdaQueryWrapper<WarehouseInRecord> qw = Wrappers.lambdaQuery(WarehouseInRecord.class)
                .eq(WarehouseInRecord::getOrganId, organId)
                .in(!warehouseIds.isEmpty(), WarehouseInRecord::getWarehouseId, warehouseIds)
                .like(StrUtil.isNotBlank(serialNum), WarehouseInRecord::getSerialNum, serialNum)
                .eq(StrUtil.isNotBlank(status), WarehouseInRecord::getStatus, status)
                .ge(StrUtil.isNotBlank(beginTime), WarehouseInRecord::getCreateTime, beginTime)
                .le(StrUtil.isNotBlank(endTime), WarehouseInRecord::getCreateTime, endTime);
        return page(page, qw);
    }

    /**
     * 新增入库记录
     * @param warehouseInRecord 入库记录
     * @param isAuto 是否一键入：1（是）2（否）
     * @return Result<Object>
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<Object> addWarehouseInRecord(WarehouseInRecord warehouseInRecord, String isAuto) {
        String message = checkInfo(warehouseInRecord);
        if (StringUtils.isNotBlank(message)) {
            return Result.fail(message);
        }

        // 新增入库记录主表
        warehouseInRecord.setSerialNum("RK-" + System.currentTimeMillis());
        warehouseInRecord.setOrganId(WebUtils.getUser().getOrganId());
        warehouseInRecord.setCreator(WebUtils.getUser().getName());
        warehouseInRecord.setCreateTime(LocalDateTime.now());
        boolean save = save(warehouseInRecord);
        // 新增入库记录细表
        PurchaseBill purchaseBill = purchaseBillService.getById(warehouseInRecord.getPurchaseBillId());
        List<WarehouseInRecordDetails> detailList = warehouseInRecord.getDetailList();
        if (save) {
            for (WarehouseInRecordDetails warehouseInRecordDetails : detailList) {
                warehouseInRecordDetails.setPurchaseBillSerialNum(purchaseBill.getSerialNum());
                warehouseInRecordDetails.setWarehouseInRecordId(warehouseInRecord.getId());
                warehouseInRecordDetails.setCreator(WebUtils.getUser().getName());
                warehouseInRecordDetails.setCreateTime(LocalDateTime.now());
            }
            warehouseInRecordDetailsService.saveBatch(detailList);
        }

        // 如果不是一键入库，则修改采购记录细表状态为已入库
        if ("2".equals(isAuto)) {
            List<Long> collect = detailList.stream().map(WarehouseInRecordDetails::getPurchaseBillDetailsId).collect(Collectors.toList());
            boolean update = purchaseBillDetailsService.update(Wrappers.lambdaUpdate(PurchaseBillDetails.class)
                    .in(PurchaseBillDetails::getId, collect)
                    .set(PurchaseBillDetails::getStatus, "1"));
            // 细表状态修改后查询主表对应的细表是不是全部入库，如果是则主表状态改为以入库
            if (update) {
                int count = purchaseBillDetailsService.count(Wrappers.lambdaQuery(PurchaseBillDetails.class)
                        .eq(PurchaseBillDetails::getPurchaseBillId, warehouseInRecord.getPurchaseBillId())
                        .eq(PurchaseBillDetails::getStatus, "0"));
                if (count == 0) {
                    purchaseBillService.update(Wrappers.lambdaUpdate(PurchaseBill.class)
                    .eq(PurchaseBill::getId, warehouseInRecord.getPurchaseBillId())
                    .set(PurchaseBill::getStatus, "4"));
                }
            }
        }
        return Result.success();
    }

    /**
     * 修改入库记录
     * @param warehouseInRecord 入库记录
     * @return Result<Object>
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<Object> updateWarehouseInRecord(WarehouseInRecord warehouseInRecord) {
        // 检查信息是否正确
        String message = checkInfo(warehouseInRecord);
        if (StringUtils.isNotBlank(message)) {
            return Result.fail(message);
        }
        // 根据主表Id删除细表记录
        boolean remove = warehouseInRecordDetailsService.remove(Wrappers.lambdaQuery(WarehouseInRecordDetails.class)
                .eq(WarehouseInRecordDetails::getWarehouseInRecordId, warehouseInRecord.getId()));
        // 新增细表
        if (remove) {
            List<WarehouseInRecordDetails> detailList = warehouseInRecord.getDetailList();
            for (WarehouseInRecordDetails warehouseInRecordDetails : detailList) {
                warehouseInRecordDetails.setWarehouseInRecordId(warehouseInRecord.getId());
            }
            warehouseInRecordDetailsService.saveBatch(detailList);
        }


        // 修改主表，并把主表状态改为未审核
        warehouseInRecord.setStatus(RecordStatus.UNCHECKED);
        warehouseInRecord.setUpdateUser(WebUtils.getUser().getName());
        warehouseInRecord.setUpdateTime(LocalDateTime.now());
        updateById(warehouseInRecord);
        return Result.success();
    }

    /**
     * 一键入库
     * @param purchaseBillDto 入库记录
     * @return Result<Object>
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<Object> autoAddWarehouseInRecord(PurchaseBillDTO purchaseBillDto) {
        // 根据食材类型将采购清单分开
        Map<String, Warehouse> warehouseMap = purchaseBillDto.getWarehouseMap();
        List<PurchaseBillDetails> detailList = purchaseBillDto.getPurchaseBill().getDetailList();
        Map<Long, WarehouseInRecord> map = new HashMap<>();
        for (PurchaseBillDetails purchaseBillDetails : detailList) {
            Long foodMaterialTypeId = purchaseBillDetails.getFoodMaterialTypeId();
            FoodMaterialType foodMaterialType = foodMaterialTypeService.getById(foodMaterialTypeId);
            List<Warehouse> warehouseList = warehouseService.list(Wrappers.lambdaQuery(Warehouse.class)
                    .like(Warehouse::getFoodMaterialTypeId, String.valueOf(foodMaterialTypeId)));
            if (warehouseList.size() == 1) {
                formatWarehouseInRecord(map, warehouseList.get(0), purchaseBillDetails);
            }else if (warehouseList.size() > 1) {
                Warehouse warehouseIn = warehouseMap.get(foodMaterialType.getName());
                if (BeanUtil.isNotEmpty(warehouseIn)) {
                    formatWarehouseInRecord(map, warehouseIn, purchaseBillDetails);
                }
            }
        }
        map.forEach((key, val) -> {
            BigDecimal amount = new BigDecimal(0);
            BigDecimal total = new BigDecimal(0);
            for (WarehouseInRecordDetails warehouseInRecordDetails : val.getDetailList()) {
                amount = amount.add(warehouseInRecordDetails.getWarehouseInAmount());
                total = total.add(warehouseInRecordDetails.getWarehouseInAmount().multiply(warehouseInRecordDetails.getPrice()));
            }
            val.setAmount(amount);
            val.setTotal(total);
            addWarehouseInRecord(val, "1");
        });
        // 修改采购清单状态
        PurchaseBill purchaseBill = purchaseBillDto.getPurchaseBill();
        purchaseBill.setStatus("4");
        purchaseBill.setUpdateUser(WebUtils.getUser().getName());
        purchaseBill.setUpdateTime(LocalDateTime.now());
        List<PurchaseBillDetails> billDetailList = purchaseBill.getDetailList();
        for (PurchaseBillDetails purchaseBillDetails : billDetailList) {
            purchaseBillDetails.setStatus("1");
            purchaseBillDetails.setUpdateUser(WebUtils.getUser().getName());
            purchaseBillDetails.setUpdateTime(LocalDateTime.now());
        }
        purchaseBillService.updateById(purchaseBill);
        purchaseBillDetailsService.updateBatchById(billDetailList);
        return Result.success();
    }

    /**
     * 构建 入库记录
     * @param map map
     * @param warehouse 仓库
     * @param purchaseBillDetails 采购清单详情
     */
    private void formatWarehouseInRecord(Map<Long, WarehouseInRecord> map,
                                         Warehouse warehouse, PurchaseBillDetails purchaseBillDetails) {
        Long warehouseId = warehouse.getId();
        WarehouseInRecord warehouseInRecord = map.get(warehouseId);
        if (BeanUtil.isEmpty(warehouseInRecord)) {
            warehouseInRecord = new WarehouseInRecord();
            warehouseInRecord.setWarehouseInDate(LocalDateTime.now().toLocalDate());
            warehouseInRecord.setType(purchaseBillDetails.getProcessPhase());
            warehouseInRecord.setWarehouseId(warehouse.getId());
            warehouseInRecord.setWarehouseName(warehouse.getName());
            warehouseInRecord.setWarehouseSerialNum(warehouse.getSerialNum());
            warehouseInRecord.setPurchaseBillId(purchaseBillDetails.getPurchaseBillId());
            PurchaseBill purchaseBill = purchaseBillService.getById(purchaseBillDetails.getPurchaseBillId());
            warehouseInRecord.setPurchaseBillSerialNum(purchaseBill.getSerialNum());
            List<WarehouseInRecordDetails> inRecordDetails = new ArrayList<>();
            formatDetails(inRecordDetails, warehouse, purchaseBillDetails);
            warehouseInRecord.setDetailList(inRecordDetails);
            map.put(warehouseId, warehouseInRecord);
        }else {
            formatDetails(warehouseInRecord.getDetailList(), warehouse, purchaseBillDetails);
        }

    }

    /**
     * 构建入库详单
     * @param inRecordDetails 详单列表
     * @param purchaseBillDetails  采购详单
     */
    private void formatDetails(List<WarehouseInRecordDetails> inRecordDetails,
                               Warehouse warehouse, PurchaseBillDetails purchaseBillDetails) {
        WarehouseInRecordDetails warehouseInRecordDetails = new WarehouseInRecordDetails();
        BeanUtil.copyProperties(purchaseBillDetails, warehouseInRecordDetails);
        warehouseInRecordDetails.setPurchaseBillDetailsId(purchaseBillDetails.getId());
        FoodMaterialType foodMaterialType = foodMaterialTypeService.getById(purchaseBillDetails.getFoodMaterialTypeId());
        warehouseInRecordDetails.setFoodMaterialTypeName(foodMaterialType.getName());
        warehouseInRecordDetails.setWarehouseId(warehouse.getId());
        warehouseInRecordDetails.setWarehouseName(warehouse.getName());
        warehouseInRecordDetails.setWarehouseSerialNum(warehouse.getSerialNum());
        // 设置入库详单的采购数量，实际入库数量，和损耗数量
        warehouseInRecordDetails.setPurchaseAmount(purchaseBillDetails.getAmount());
        warehouseInRecordDetails.setWarehouseInAmount(purchaseBillDetails.getWarehouseInAmount());
        warehouseInRecordDetails.setWasteAmount(purchaseBillDetails.getWasteAmount());
        inRecordDetails.add(warehouseInRecordDetails);
    }

    /**
     * 一键入库食材信息检查
     * @param purchaseBill 采购清单
     * @return String
     */
    @Override
    public Result<Object> checkInfoForAutoAdd(PurchaseBill purchaseBill) {
        // 根据食材类型查询所属的仓库
        List<PurchaseBillDetails> detailList = purchaseBill.getDetailList();
        if (detailList.isEmpty()) {
            return Result.fail("采购清单详情不能为空！");
        }
        List<Map<String, Object>> mapList = new ArrayList<>();
        for (PurchaseBillDetails purchaseBillDetails : detailList) {
            Long foodMaterialTypeId = purchaseBillDetails.getFoodMaterialTypeId();
            FoodMaterialType foodMaterialType = foodMaterialTypeService.getById(foodMaterialTypeId);
            List<Warehouse> warehouseList = warehouseService.list(Wrappers.lambdaQuery(Warehouse.class)
                    .like(Warehouse::getFoodMaterialTypeId, String.valueOf(foodMaterialTypeId)));
            // 如果没有对应的仓库，提醒用户创建仓库，或关联已有的仓库到相应的食材类型
            if (warehouseList.isEmpty()) {
                return Result.fail("食材【" + purchaseBillDetails.getFoodMaterialName() + "】所属的食材类型【"
                        + foodMaterialType.getName() + "】还没有对应的仓库，请先创建对应的仓库或关联已有的仓库到该食材类型！");
            }
            // 如果对应的仓库有两个以上，提醒用户选择仓库
            boolean flag = true;
            for (Map<String, Object> map : mapList) {
                if (map.get("foodMaterialType").equals(foodMaterialType.getName())) {
                    flag = false;
                    break;
                }
            }
            if (warehouseList.size() > 1 && flag) {
                Map<String, Object> map = new HashMap<>(2);
                map.put("foodMaterialType", foodMaterialType.getName());
                map.put("warehouseList", warehouseList);
                mapList.add(map);
            }
        }
        if (!mapList.isEmpty()) {
            String message = "以下食材类型都关联了多个仓库，请选择要入库的仓库!";
            return Result.success(mapList, message);
        }

        return Result.success();
    }

    /**
     * 删除入库清单
     * @param id 采入库清单Id
     * @return Result<Object>
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<Object> deleteWarehouseInRecord(Long id) {
        // 只能删除未审核 和 审核不通过的入库清单
        WarehouseInRecord warehouseInRecord = getById(id);
        if (!(RecordStatus.UNCHECKED.equals(warehouseInRecord.getStatus())
                || RecordStatus.CHECK_NOT_PASS.equals(warehouseInRecord.getStatus()))) {
            return Result.fail("只能删除未审核和审核不通过的入库清单");
        }
        List<WarehouseInRecordDetails> detailsList = warehouseInRecordDetailsService.list(Wrappers.lambdaQuery(WarehouseInRecordDetails.class)
                .eq(WarehouseInRecordDetails::getWarehouseInRecordId, id));

        // 删除入库清单时，把对应的采购清单主表和细表状态修改
        List<Long> collect = detailsList.stream().map(WarehouseInRecordDetails::getPurchaseBillDetailsId).collect(Collectors.toList());
        boolean update = purchaseBillDetailsService.update(Wrappers.lambdaUpdate(PurchaseBillDetails.class)
                .in(PurchaseBillDetails::getId, collect)
                .set(PurchaseBillDetails::getStatus, "0"));
        if (update) {
            int count = purchaseBillDetailsService.count(Wrappers.lambdaQuery(PurchaseBillDetails.class)
                    .eq(PurchaseBillDetails::getPurchaseBillId, warehouseInRecord.getPurchaseBillId())
                    .eq(PurchaseBillDetails::getStatus, "1"));
            String status;
            if (count == 0) {
                status = "2";
            }else {
                status = "3";
            }
            purchaseBillService.update(Wrappers.lambdaUpdate(PurchaseBill.class)
                    .eq(PurchaseBill::getId, warehouseInRecord.getPurchaseBillId())
                    .set(PurchaseBill::getStatus, status));
        }
        // 删除细表
        boolean remove = warehouseInRecordDetailsService.remove(Wrappers.lambdaUpdate(WarehouseInRecordDetails.class)
                .eq(WarehouseInRecordDetails::getWarehouseInRecordId, id));
        // 删除主表
        if (remove) {
            removeById(id);
        }
        return Result.success();
    }

    /**
     * 审核入库记录
     * @param warehouseInRecord 入库记录信息
     * @return Result<Object>
     */
    @Override
    public Result<Object> checkWarehouseInRecord(WarehouseInRecord warehouseInRecord) {
        UserBean user = WebUtils.getUser();
        warehouseInRecord.setCheckBy(user.getName());
        warehouseInRecord.setCheckById(user.getId());
        warehouseInRecord.setCheckTime(LocalDateTime.now());
        warehouseInRecord.setUpdateUser(user.getName());
        warehouseInRecord.setUpdateTime(LocalDateTime.now());
        updateById(warehouseInRecord);
        return Result.success();
    }

    /**
     * 新增入库记录时检查信息是否正确
     * @param warehouseInRecord 入库记录信息
     * @return String
     */
    private String checkInfo(WarehouseInRecord warehouseInRecord) {
        String message = null;
        List<WarehouseInRecordDetails> detailList = warehouseInRecord.getDetailList();
        if (detailList.isEmpty()) {
            message = "入库记录详细信息不能为空";
        }
        // 检查食材类型是否和仓库关联的食材类型一致
        Warehouse warehouse = warehouseService.getById(warehouseInRecord.getWarehouseId());
        String typeIds = warehouse.getFoodMaterialTypeId();
        for (WarehouseInRecordDetails warehouseInRecordDetails : detailList) {
            String foodMaterialTypeId = String.valueOf(warehouseInRecordDetails.getFoodMaterialTypeId());
            if (!typeIds.contains(foodMaterialTypeId)) {
                message = "食材类型与仓库关联的食材类型不符";
                break;
            }
        }
        return message;
    }

}
